package com.horizon.cloud.standard.hibernate;

import com.horizon.cloud.common.distribute.idGenerator.IdGenerator;
import com.horizon.cloud.distribute.IdGenerator.impl.SnowflakeIdGenerator;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IncrementGenerator;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 通用的 分布式 Hibernate ID 生成器
 */
public class CommonSnowflakeIdGenerator extends IncrementGenerator {

    /**
     * 生成器的 map 缓存
     * key 为 PO 类名,value 为分布式ID生成器
     */
    private Map<String, SnowflakeIdGenerator> generatorMap = new LinkedHashMap<>();

    @Override
    public synchronized Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        /**
         * 以po类的类名，作为id的类型，generator的object参数就是对应生成id的po类
         * 每一个类型对应一个分布式id生成器
         */
        String type = object.getClass().getSimpleName();
        Serializable id = null;

        /**
         * 从map中取得分布式id生成器
         */
        IdGenerator idGenerator = getFromMap(type);

        /**
         * 调用自定义的nextId方法，即zk+snowflake算法生成po类的主键id
         * nextid中就是在zk中创建一个临时顺序节点，然后截取节点的序号作为workid机器码
         */
        id = idGenerator.nextId();
        if(null != id) {
            return id;
        }

        /**
         * 如果生成失败，则通过父类生成
         * 这里先获取了EntityPersister然后获取了类的元数据，
         * 用元数据的getIdentifier方法来获取当前这个po类的主键，查看主键是否生成
         */
        id = session.getEntityPersister(null, object)
                .getClassMetadata().getIdentifier(object, session);
        return id != null ? id: super.generate(session, object);
    }

    /**
     * 从map中获取缓存的分布式id生成器，没有就创建一个
     * @param type
     * @return
     */
    public synchronized IdGenerator getFromMap(String type) {
        if (generatorMap.containsKey(type))
        {
            return generatorMap.get(type);
        }

        /**
         * 创建分布式 ID 生成器，并且存入 map
         */
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(type);
        generatorMap.put(type, idGenerator);
        return idGenerator;
    }
}
